#include "tknetbusi.h"
#include "tkconstants.h"
#include "tkerror.h"
#include "tklog.h"

#include "clientinfo.dsc.h"
#include "memberlist.dsc.h"
#include "message.dsc.h"
#include "tknetbusi.h"

int hd_get_client_information(struct epoll_env *env, struct custom_socket *s, int event_type);
int hd_send_clients_list_information(struct epoll_env *env, struct custom_socket *s, int event_type);
int hd_communicating(struct epoll_env *env, struct custom_socket *s, int event_type);

extern void do_broadcast_message(struct tk_connect *conn, struct tk_message *msg);
extern struct member* main_insert_member(struct member *mem);

/* in main.c */
void do_broadcast_message(struct tk_connect *conn, struct tk_message *msg);

int do_after_accept_entry(struct epoll_env *env, struct custom_socket *s)
{
	int r = 0;
	struct tk_connect *conn = NULL;

	conn = get_tk_connect(s);
	conn->stage = TK_CONNECT_SYNC_CLIENT_INFO;

	/* add read event */
	r = epoll_add_read_event(env, s->event);
	if (r) {
		elog("add read event failed");
		tk_connection_error(env, conn);

		return -1;
	}
				
	/* set read handler */	
	s->handler = hd_get_client_information;

	return 0;
}

int do_after_connect_entry(struct custom_socket *s)
{
	return 0;
}

/* first application handler */
/*
1. get client information 
2. insert member data to global member list 
3. del write and add read event 
4. change handler to hd_send_clients_list_information
note: before hd_send_clients_list_information the main process clients information  
*/
int hd_get_client_information(struct epoll_env *env, struct custom_socket *s, int event_type)
{
	struct tk_connect *conn = (struct tk_connect*)s->ptr;
	struct tk_message *msg = &conn->recv_msg;
	void *event = s->event;
	struct tk_message tmp_msg;
	clientinfo client;
	memberlist *memlist;
	struct member *mem;
	int len;
	int r, rc;
	
        if (event_type != EVENT_READ) {
                elog("hd_get_client_information raised by read event");
		conn->failed = 1;

                return TK_ERROR;
        }

	do {

		r = tk_do_recv_msg(conn);
		if (r == TK_RECV_BODY_DONE) {
			len = msg->body_len;
			memset(&client, 0x00, sizeof(client));
			r = DSCDESERIALIZE_JSON_clientinfo("GBK", msg->body, &len, &client);
			if (r) {
				elog( __FILE__ , __LINE__ , "r=%d" , r );
				return TK_JSON_DESERIALIZE;
			}

			ilog("%d - %s", client.clientid, client.clientname);

			conn->client_ptr = malloc(sizeof(struct member));
			if (conn->client_ptr == NULL) {
				elog("get struct member failed");
				conn->failed = 1;
				return TK_ERROR;
			}

			mem = (struct member*)conn->client_ptr;

			memset(mem, 0x00, sizeof(struct member));
			mem->clientid = client.clientid;
			strcpy(mem->clientname, client.clientname); 
			strcpy(mem->ip, s->addr.ipv4);

			/* do send information */
			/* add write handler and del read handler */
			(VOID)epoll_del_read_event(env, (void*)s->event);
			(VOID)epoll_add_write_event(env, (void*)s->event);
			s->handler = hd_send_clients_list_information;
			conn->stage = TK_CONNECT_SYNC_CLIENT_INFO_DONE;
		}
		else if (r == TK_ERROR) {
			elog("hd_get_client_information failed");
			conn->failed = 1;
			return TK_ERROR;
		}	
		else if (r == TK_DONE) {
			elog("hd_get_client_information remote close");
			conn->finished = 1;
			break;
		}

	} while (r == TK_AGAIN || r == TK_OK);

        return TK_OK;
}

/*
1. sync client list information to new client 
2. after the new client sync the client list information, the main process will sync the new client to other communicating stage client
*/
int hd_send_clients_list_information(struct epoll_env *env, struct custom_socket *s, int event_type)
{
	int r;
	struct tk_connect *conn = (struct tk_connect*)s->ptr;
	struct member *mem;
	struct member *insert_mem;
	void *event = s->event;
	clientinfo client;

	conn->stage = TK_CONNECT_SYNC_CLIENTS_LIST;

        if (event_type & EVENT_LOCAL_ERROR) {
                conn->failed = 1;
                return TK_OK;
        }
        else if (event_type & EVENT_REMOTE_CLOSE) {
                conn->finished = 1;
                return TK_OK;
        }

	if (event_type != EVENT_WRITE) {
                elog("hd_send_clients_list_information raised by write event");
                return TK_ERROR;
	}

	/* send initialize clients information */
	do {
		/**
                1. TK_AGAIN (internal buffer is full)
                2. TK_ERROR(errno)
                3. TK_DONE(no use)
                4. TK_OK (head sent) should delete event(if non-blocking mode)
                5. TK_NO_MORE_SEND_MSG (attention: other success status) should delete event(if non-blocking mode)
                6. TK_SEND_BODY_DONE: should delete event(if non-blocking mode)
                at most send one message
                **/
		r = tk_do_send_msg(conn);
		if (r == TK_NO_MORE_SEND_MSG) {
			/* del write event and add read event */
			(VOID)epoll_add_read_event(env, (void*)s->event);
			(VOID)epoll_del_write_event(env, (void*)s->event);
			/* change handler */
			s->handler = hd_communicating;

			/* insert app environment's connect list */
			main_insert_connect(&conn->list_node);

			/* insert app environment's memeber list */
			mem = (struct member*)conn->client_ptr;

                        insert_mem = main_insert_member(mem);
                        if (insert_mem == NULL) {
                                conn->failed = 1;
                                return TK_ERROR;
                        }
                        else {
				free(mem);
                                conn->client_ptr = (void*)insert_mem;
                        }
			conn->stage = TK_CONNECT_SYNC_CLIENTS_LIST_DONE;
		}
		else if (r == TK_OK) {
			elog("not possible tk_ok");
			conn->failed = 1;
			
			return TK_ERROR;
		}
		else if (r == TK_ERROR) {
			conn->failed = 1;
			
			return TK_ERROR;
		}

	} while (r == TK_AGAIN);

	return TK_OK;
}

/* catch read and write event */
int hd_communicating(struct epoll_env *env, struct custom_socket *s, int event_type)
{
	int r = 0;
	int len;
	struct tk_connect *conn = (struct tk_connect*)s->ptr;
	void *event = s->event;
	message msg;
	struct tk_message tk_msg;

	conn->stage = TK_CONNECT_COMMUNICATING;

	if (event_type & EVENT_LOCAL_ERROR) {
		conn->failed = 1;
		return TK_OK;
	}
	else if (event_type & EVENT_REMOTE_CLOSE) {
		conn->finished = 1;
		return TK_OK;
	}

	if ((event_type & EVENT_READ) || is_readable(event)) {
		/** 
		1. TK_OK (head received) should delete event(if non-blocking mode) (no use 20111126)
		2. TK_AGAIN (internal buffer is empty)
		3. TK_ERROR(errno)
		4. TK_DONE      (remote close)
		5. TK_RECV_BODY_DONE should delete event(if non-blocking mode)
		**/
		/*
		do {
		*/
			r = tk_do_recv_msg(conn);
			if (r == TK_RECV_BODY_DONE) {
				memset(&msg, 0x00, sizeof(msg));
				len = conn->recv_msg.body_len;
				r = DSCDESERIALIZE_JSON_message("GBK", conn->recv_msg.body, &len, &msg);
				if (r) {
					elog( __FILE__ , __LINE__ , "r=%d" , r );
					return TK_JSON_DESERIALIZE;
				}

				ilog("group[%d] - groupname[%s]\nclientid[%d] - clientname[%s]\ncontentlen[%d] - content[%s]", 
					msg.groupid, msg.groupname, msg.clientid, msg.clientname, msg.contentlen, msg.content);

				/* broadcast to other clients, now client may unknow who send message(fixme) */
				r = build_tk_message_with_message(&tk_msg, &msg);
				if (r) {
					conn->failed = 1;
					elog("build_tk_message_with_message failed\n");
				}
				do_broadcast_message(conn, &tk_msg);
				
				/* internal buffer not full and we can receive data, but find a compelete message */
				set_readable(event, 1);
			}
			else if (r == TK_ERROR) {
				conn->failed = 1;
				elog("communicating read failed");
				return TK_ERROR;
			}
			else if (r == TK_DONE) {
				elog("hd_get_client_information remote close");
				conn->finished = 1;
			}
			else if (r == TK_AGAIN) {
				set_readable(event, 0);
			}

		/*
		} while (r == TK_OK);	
		*/
	}
	if ((event_type & EVENT_WRITE) || is_writable(event)) {
		/**
		1. TK_AGAIN (internal buffer is full)
		2. TK_ERROR(errno)
		3. TK_DONE(no use)
		4. TK_OK (head sent) should delete event(if non-blocking mode) (no use 20111126)
		5. TK_NO_MORE_SEND_MSG (attention: other success status) should delete event(if non-blocking mode)
		6. TK_SEND_BODY_DONE: should delete event(if non-blocking mode)
		at most send one message
		**/
		/*
		do {
		*/
			r = tk_do_send_msg(conn);
			if (r == TK_ERROR) {
				conn->failed = 1;
				elog("communicating write failed");
				return TK_ERROR;	
			}
			else if (r == TK_SEND_BODY_DONE) {
				/* continue to send next message */
				continue;
			}
			else if (r == TK_NO_MORE_SEND_MSG) {
				/* internal buffer not full, but no data to send */
				/* 
				set_writable(event, 1); 
				directly delete write event, when message added by main process, read event added auto 
				*/

				epoll_del_write_event(env, event);
			}
			if (r == TK_AGAIN) {
				/* set_writable(event, 0);  write event will added in add msg */
			}

		/*
		} while (r == TK_OK);
		*/
	}
	
	return TK_OK;
}

int hd_handler_entry(struct epoll_env *env, struct custom_socket *s, int event_type)
{
	struct tk_connect *conn = (struct tk_connect*)s->ptr;

	if (event_type & EVENT_LOCAL_ERROR) {
		conn->failed = 1;
	}
	else if (event_type & EVENT_REMOTE_CLOSE) {
		conn->finished = 1;
	}	

	if (conn->finished) {
		return TK_OK;
	}
	else if (conn->failed) {
		return TK_ERROR;	
	}

	return s->handler(env, s, event_type);		
}
